終於到了我們的元件篇啦!!!
今天是第一個元件,所以稍微簡單一點。
我們要來做下載的進度條~
我們這次要使用的專案環境是 Vue 的,所以之前建立過的那個專案就不沿用囉!
而 Tailwind + Vue 的專案要快速建立就得用到 Creator
啦!
Creator 連結在這邊: 點我
進到 Creator 後,我這邊專案名稱是叫做 beautiful-components-libs
,然後 template 選擇 Vite 分類下的 Vue。
都 OK 之後,大力按下複製然後貼到你的 Terminal (cmd) 中,讓他開始建立專案並開啟 dev server。
開啟後,我們瀏覽網站就會看到這個畫面:
那這樣前置作業就完成啦~
接著開啟 vs code 準備開發!
(如果對這些步驟或是對 Creator 還不熟悉的人,建議你回去複習兔兔的 Day 18:「極速開發」- Vitawind
)
首先,我們要先建立空白元件,
這樣畫面上就可以同步看到我們的修改內容。
先在專案裡的 ./src/components
資料夾中新增一個 ProgressBar.vue
的元件:
完成後,增添以下內容:
<template>
</template>
<script>
export default {
name: "ProgressBar",
}
</script>
<style>
</style>
接著我們就可以把這個元件新增到畫面中,打開 App.vue
然後改成以下內容:
<template>
<div :class="[
'w-screen h-screen',
'flex flex-col',
'justify-center items-center',
'gap-5',
]">
<ProgressBar />
</div>
</template>
<script>
import ProgressBar from './components/ProgressBar.vue'
export default {
components: {
ProgressBar
}
}
</script>
這時候畫面上 ... 是空白的!
對,因為我們把 HelloWorld.vue 從畫面上去掉了,加入了我們建立的 ProgressBar
元件,然後因為元件內容都還沒有做,所以畫面是空白的。
再來,就要來開始做我們的進度條囉!
我們先來看一下,進度條可能會有的樣子!
所以很明顯,這就是要分成三層,
既然知道了,就 div 大法直接建 3 層!
像這樣:
<template>
<div>
<div>
<div />
</div>
</div>
</template>
<script>
export default {
name: "ProgressBar",
}
</script>
建好之後,都先填上背景顏色:
<div :class="[
'bg-gray-200'
]">
<div :class="[
'bg-green-800'
]">
<div :class="[
'bg-green-500'
]" />
</div>
</div>
因為現在都還沒有寬度高度,所以畫面上還是沒有東西。 我們幫最外層設定高度 w-80
並增加一點內距 p-5
:
<div :class="[
'w-80 p-5',
'bg-gray-200'
]">
<div :class="[
'bg-green-800'
]">
<div :class="[
'bg-green-500'
]" />
</div>
</div>
看起來有點 ... 樣子?
還沒,我們再來幫進度條背景和進度條本體設定寬度高度,進度條背景的寬高設為 w-full h-2
,然後進度條本體的寬高設定為 w-1/3 h-full
:
<div :class="[
'w-80 p-5',
'bg-gray-200'
]">
<div :class="[
'w-full h-2',
'bg-green-800'
]">
<div :class="[
'w-1/3 h-full',
'bg-green-500'
]" />
</div>
</div>
這次是不是真的有點 fu 了?
對啦~一定有啦~我知道啦~
可是根據小米定律,要好看的話肯定要加上什麼?
大聲點,我聽不到! 梁靜茹也聽不到!
嘿啦~就是圓角
啦!
我們幫最外層加上大量級的圓角 rounded-lg
,然後幫進度條背景和進度條本體都加上 rounded-full
:
<div :class="[
'w-80 p-5',
'bg-gray-200',
'rounded-lg'
]">
<div :class="[
'w-full h-2',
'bg-green-800',
'rounded-full'
]">
<div :class="[
'w-1/3 h-full',
'bg-green-500',
'rounded-full'
]" />
</div>
</div>
嗯~~~很可以。
是不是! 我就說小米定律超有效的啦!
質感整個大提升了。
但現在只是固定的畫面,所以我們要讓它可以實際使用!
要讓它動起來了!
接下來就是要寫 Vue 的部分了。
這時候可以仔細的思考一下,
進度條會需要什麼東西?
答案就是:最小值
、最大值
、現在值
。
所以我們就幫元件加上 props,分別是 minVal
、maxVal
、currentVal
:
<script>
export default {
name: "ProgressBar",
props: ["minVal","maxVal","currentVal"],
}
</script>
加好之後,我們需要算進度是多少 % 。
在 data 中建立一個變數 percent 來儲存算完的結果
,預設值是 0。
但是光是建立好變數,還沒建立算式呢! 這時候我們要使用之前沒說過的東西:watch
來完成。
之前在 Day 20 有談過類似的概念,而 Vue 中的 watch 呢就是可以用來監看某個變數或物件是否有變動
,然後觸發你所指定的方法。
因為我們這裡需要在 currentVal 變動時重新計算進度是多少 % ,所以我們寫在 watch 之中:
<script>
export default {
name: "ProgressBar",
props: ["minVal","maxVal","currentVal"],
data() {
return {
percent: 0,
}
},
watch: {
currentVal(newVal, oldVal) {
this.percent = newVal <= this.minVal ? 0 : (newVal-this.minVal)*100/(this.maxVal-this.minVal)
}
}
}
</script>
最後,因為元件建立時載入初始值的當下不算是變數內容的變動,所以我們要在元件掛載時重新計算一次:
<script>
export default {
name: "ProgressBar",
props: ["minVal","maxVal","currentVal"],
data() {
return {
percent: 0,
}
},
mounted() {
this.percent = this.currentVal <= this.minVal ? 0 : (this.currentVal-this.minVal)*100/(this.maxVal-this.minVal)
},
watch: {
currentVal(newVal, oldVal) {
this.percent = newVal <= this.minVal ? 0 : (newVal-this.minVal)*100/(this.maxVal-this.minVal)
}
}
}
</script>
那麼我們就 ... 啊!
修但幾咧,差點就忘了最重要最重要的點!
我們的 % 算完還沒讓它應用到高度上啊 XD
所以,我們貼心的幫進度條本體綁定 style
,用 percent 來動態改變寬度:
<div :class="[
'w-80 p-5',
'bg-gray-200',
'rounded-lg'
]">
<div :class="[
'w-full h-2',
'bg-green-800',
'rounded-full'
]">
<div
:class="[
'w-1/3 h-full',
'bg-green-500',
'rounded-full'
]"
:style="{
width: percent + '%'
}"
/>
</div>
</div>
這樣就真的完成了!
最後元件的完整內容會是這樣:
<template>
<div :class="[
'w-80 p-5',
'bg-gray-200',
'rounded-lg'
]">
<div :class="[
'w-full h-2',
'bg-green-800',
'rounded-full'
]">
<div
:class="[
'w-1/3 h-full',
'bg-green-500',
'rounded-full'
]"
:style="{
width: percent + '%'
}"
/>
</div>
</div>
</template>
<script>
export default {
name: "ProgressBar",
props: ["minVal","maxVal","currentVal"],
data() {
return {
percent: 0,
}
},
mounted() {
this.percent = this.currentVal <= this.minVal ? 0 : (this.currentVal-this.minVal)*100/(this.maxVal-this.minVal)
},
watch: {
currentVal(newVal, oldVal) {
this.percent = newVal <= this.minVal ? 0 : (newVal-this.minVal)*100/(this.maxVal-this.minVal)
}
}
}
</script>
元件完成之後,我們就可以來測試啦!
再測試之前,我們要先來完善一下我們的測試環境。
回到 App.vue
,我們來增加一個拉桿的 input 元素,不然一直改來改去很累,不如用拉的來改變數值!
<div :class="[
'w-screen h-screen',
'flex flex-col',
'justify-center items-center',
'gap-5',
]">
<input type="range" />
<ProgressBar />
</div>
加好之後,我們也需要從外部幫他們兩個元件設定最小值、最大值,然後讓進度條元件的 currentVal 與拉桿的當前值同步,最後就會是這個樣子:
<template>
<div :class="[
'w-screen h-screen',
'flex flex-col',
'justify-center items-center',
'gap-5',
]">
<input type="range" v-model="val" :min="min" :max="max" />
<ProgressBar :currentVal="val" :minVal="min" :maxVal="max" />
</div>
</template>
<script>
import ProgressBar from './components/ProgressBar.vue'
export default {
data() {
return {
min: 0,
max: 200,
val: 100,
}
},
components: {
ProgressBar,
}
}
</script>
那麼可以測試了!
我們回到畫面上玩玩~
耶~大成功!
雖然像是做完了,不過還有一些可以再加強的地方!
畢竟這樣還是不夠靈活、不夠華麗呀~
所以最簡單的,我們先加上個過渡效果
,且 duration 設為 0.5 s:
<div :class="[
'w-80 p-5',
'bg-gray-200',
'rounded-lg'
]">
<div :class="[
'w-full h-2',
'bg-green-800',
'rounded-full'
]">
<div
:class="[
'w-1/3 h-full',
'bg-green-500',
'rounded-full',
'transition-all duration-500'
]"
:style="{
width: percent + '%'
}"
/>
</div>
</div>
加完之後,應該覺得順暢多了吧?
「是沒錯啦,華麗的部份解決了 ... 但兔兔你說的靈活度呢?」
靈活度嗎,好!
這就要靠 slot 啦~
我們可以運用 slot 的 props,把資料從內部拉出來外部使用
,像是:
<div :class="[
'w-80 p-5',
'bg-gray-200',
'rounded-lg',
'flex flex-col',
'justify-center items-center',
'gap-3'
]">
<slot
:minVal="minVal"
:maxVal="maxVal"
:currentVal="currentVal"
:percent="percent"
/>
<div :class="[
'w-full h-2',
'bg-green-800',
'rounded-full'
]">
<div
:class="[
'w-1/3 h-full',
'bg-green-500',
'rounded-full',
'transition-all duration-500'
]"
:style="{
width: percent + '%'
}"
/>
</div>
</div>
然後為了讓文字和進度條可以直向排列,我還另外在外框多下了
flex flex-col
的樣式,然後記得給一點空隙,才不會看起來太擠。
這樣做之後,我們就可以弄出像這樣子的效果:
<ProgressBar
:currentVal="val" :minVal="min" :maxVal="max"
v-slot="{ percent }"
>
已下載 {{ percent }} %
</ProgressBar>
不僅如此,
也可以輕鬆修改出像是遊戲載入資源的進度條:
<ProgressBar
:currentVal="val" :minVal="min" :maxVal="max"
v-slot="{ maxVal, currentVal }"
>
正在準備資源 ({{currentVal}} / {{maxVal}})
</ProgressBar>
那就會看起來像是這樣~
這麼一來就完全做完啦!
好的,那麼今天的進度條就是這麼輕鬆簡單~
明天的也很簡單喔,
我們要來完成各種按鈕的變化!
是不是躍躍欲試啦?
那我就不說這麼多了,趕快留時間給你去做做看!
關於兔兔們:
( # 兔兔小聲說 )
今天兔兔喉嚨燒聲,說不出話。
但是可以唱歌!(欸你這明明就是選擇性的!)
Hi 兔兔
今天跟著做進度條,發現有個 bug
// 如果今天最小值給的不是 0,帶入公式
// min=10 ,max=100
// current=10 會是 11% 沒有歸零
// current=90 就會是 100%
this.percent = Number(this.currentVal*100/(this.maxVal-this.minVal))
是否優化成這樣會更好
this.percent = +this.currentVal <= this.minVal ? 0 : +this.currentVal-this.minVal/(this.maxVal-this.minVal)
對,我沒注意到這個小細節
會,你優化成這樣是可以的!
我更新上去,謝謝~
不過你的公式還要再乘 100 哦
但還是謝謝你!